﻿using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using GCFramework.Singleton;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;

namespace GCFramework.Utility.ObjectPool
{
    public class PoolManager : SingletonMono<PoolManager>
    {
        // 对象池管理容器
        private readonly Dictionary<string, List<Poolable>> _objectPool = new Dictionary<string, List<Poolable>>();
        private readonly Dictionary<string, Transform> _poolObjRootDic = new Dictionary<string, Transform>();

        // 对象池根节点
        public Transform PoolRoot { get; private set; }
        public List<Poolable> preRegisterPoolObjList;
        
        public int preCacheCount = 10;
        public int poolMaxCount = 1000;
        // [Title("对象池资源释放")] 
        // [Tooltip("检测间隔, 避免多次扫描对象池，降低性能")]
        // public float checkInterval = 5f;
        // [Tooltip("释放资源间隔，防止多次释放造成卡顿")]
        // public float clearInterval = 10f;
        // [Tooltip("最小触发清理的未使用对象总数量")]
        // public int minUnUsingNumber = 100;
        // [Tooltip("最大触发清理的使用中对象总数量")]
        // public int maxUsingNumber = 20;
        // [Tooltip("是否启用释放机制")]
        // public bool enableClearSystem = false;

        private int _markPoolObjTotalNum;
        private bool _isCanClearPool;
        private float _lastCheckTime;
        private float _lastClearTime;

        protected override void Awake()
        {
            base.Awake();
            PoolRoot = new GameObject("Pool Root").transform;
            PoolRoot.SetParent(transform);
            InitPool();
        }

        private void Update()
        {
            // AutoClearPool();
        }

        public bool HadRoot(string poolKey)
        {
            return !string.IsNullOrEmpty(poolKey) && _poolObjRootDic.ContainsKey(poolKey);
        }

        public string GetKey(Poolable poolObj)
        {
            if (poolObj == null)
                return string.Empty;
            return poolObj.name.Replace("(Clone)", "");
        }

        public void FreeObj(Poolable obj)
        {
            _markPoolObjTotalNum--;
            Destroy(obj.gameObject);
        }

        private void InitPool()
        {
            // 创建根节点
            for (int i = 0; i < preRegisterPoolObjList.Count; i++)
            {
                var poolObj = preRegisterPoolObjList[i];
                if (!HadRoot(GetKey(poolObj))) 
                    CreateObjRoot(poolObj);
                _objectPool.Add(poolObj.name, new List<Poolable>());
                for (int j = 0; j < preCacheCount; j++)
                {
                    var objIns = CreateObject(poolObj);
                    objIns.OnReturn();
                    _objectPool[poolObj.name].Add(objIns);
                }
            }
        }

        private void CreateObjRoot(Poolable poolObj)
        {
            if (poolObj == null)
                return;
            
            var root = new GameObject($"{poolObj.gameObject.name} Root");
            root.transform.SetParent(PoolRoot);
            _poolObjRootDic.Add(poolObj.name, root.transform);
        }

        private Poolable CreateObject(Poolable objPrefab)
        {
            var objIns = Instantiate(objPrefab, _poolObjRootDic[objPrefab.name]);
            _markPoolObjTotalNum++;
            objIns.OnCreate();
            return objIns;
        }

        #region Return & Get & Release

        public void ReturnToPool(Poolable poolObj)
        {
            if (poolObj == null)
            {
                Debug.LogError("[ObjectPool] 放入了一个空引用！");
                return;
            }

            string key = GetKey(poolObj);
            if (_objectPool.TryGetValue(key, out var pool))
            {
                if (pool.Count < poolMaxCount)
                {
                    if (HadRoot(key) && poolObj.transform.parent != _poolObjRootDic[key])
                        poolObj.transform.SetParent(_poolObjRootDic[key]);
                    poolObj.OnReturn();
                    pool.Add(poolObj);
                }
                else
                {
                    poolObj.Release();
                }
            }
            else
            {
                // 没有注册过，则创建Root之后 再加入对象池
                if (!HadRoot(key)) CreateObjRoot(poolObj);
                _objectPool.Add(key, new List<Poolable>());
                var objIns = CreateObject(poolObj);
                objIns.OnReturn();
                _objectPool[key].Add(objIns);
            }
        }

        public T GetFromPool<T>(Poolable poolObj) where T : Poolable
        {
            if (poolObj == null)
            {
                Debug.LogError("[ObjectPool] 无法根据一个空引用获取对象！");
                return null;
            }

            string key = GetKey(poolObj);
            Poolable objIns = null;
            List<Poolable> pool;
            if (_objectPool.TryGetValue(key, out pool))
            {
                if (pool.Count == 0)
                {
                    objIns = CreateObject(poolObj);
                }
                else
                {
                    objIns = pool.First();
                    pool.Remove(objIns);
                }
            }
            else
            {
                if (!HadRoot(key)) CreateObjRoot(poolObj);
                _objectPool.Add(key, new List<Poolable>());
                objIns = CreateObject(poolObj);
            }

            if (objIns)
            {
                objIns.OnGet();
            }
            return (T)objIns;
        }

        public void Clear(Poolable poolObj)
        {
            if (poolObj == null)
                return;
            if (_objectPool.TryGetValue(GetKey(poolObj), out var pool))
            {
                if (pool.Count <= 10)
                    return;
                for (int i = pool.Count - 1; i >= 10; i--)
                {
                    if (pool[i]) pool[i].Release();
                }
            }
        }

        #endregion

        #region Auto Release System
        //
        // private void AutoClearPool()
        // {
        //     if (!enableClearSystem || !CanClearCondition())
        //         return;
        //
        //     if (Time.time > _lastClearTime + clearInterval)
        //     {
        //         RunObjPoolClearProgram();
        //         _lastClearTime = Time.time;
        //     }
        // }
        //
        // private void RunObjPoolClearProgram()
        // {
        //     enableClearSystem = false;
        //     List<Poolable> clearList = new List<Poolable>();
        //     foreach (var poolPair in _objectPool)
        //     {
        //         var pool = poolPair.Value;
        //         int clearNum = Mathf.CeilToInt(pool.Count * 0.2f);
        //         if (pool.Count - clearNum < 10)
        //             continue;
        //         for (int i = pool.Count - 1; i >= pool.Count - clearNum && i >= 0; i--)
        //         {
        //             clearList.Add(pool[i]);
        //             pool.RemoveAt(i);
        //         }
        //
        //         for (int i = clearList.Count - 1; i >= 0; i--)
        //         {
        //             FreeObj(clearList[i]);
        //         }
        //     }
        //
        //     enableClearSystem = true;
        // }
        //
        // private bool CanClearCondition()
        // {
        //     if (Time.time < _lastCheckTime + checkInterval)
        //         return false;
        //     
        //     int count = 0;
        //     foreach (var pool in _objectPool)
        //     {
        //         if (pool.Value != null)
        //             count += pool.Value.Count;
        //     }
        //
        //     int usingNum = _markPoolObjTotalNum - count;
        //     // 如果未被使用数量大于最小触发清理数量，并且使用中数量小于最大使用数量，那么就可以触发清理机制了
        //     return count > minUnUsingNumber && usingNum < maxUsingNumber;
        // }

        #endregion
    }
}